package cn.iclass.common.aspect;

import cn.hutool.json.JSONUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description: 打印请求参数
 * @author: alan
 * @date: 2019/6/27 4:25 PM
 */

@Order(1)
@Slf4j
@Aspect
@Component
public class WebLogAspect {


	/**
	 * 以 controller 包下定义的所有请求为切入点
	 */
	@Pointcut("execution(public * cn.iclass.modules..*.controller..*.*(..))")
	public void webLog() {
		log.info("webLog aspect");
	}

	@Around("webLog()")
	public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		long start = System.currentTimeMillis();
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		assert attributes != null;
		HttpServletRequest request = attributes.getRequest();
		Object result = proceedingJoinPoint.proceed();
		RequestInfo requestInfo = new RequestInfo();
		requestInfo.setIp(request.getRemoteAddr());
		requestInfo.setUrl(request.getRequestURL().toString());
		requestInfo.setHttpMethod(request.getMethod());
		requestInfo.setClassMethod(String.format("%s.%s", proceedingJoinPoint.getSignature().getDeclaringTypeName(),
				proceedingJoinPoint.getSignature().getName()));
		requestInfo.setRequestParams(getRequestParamsByProceedingJoinPoint(proceedingJoinPoint));
		requestInfo.setResult(result);
		requestInfo.setTimeCost(System.currentTimeMillis() - start);
		log.info(JSONUtil.toJsonPrettyStr(requestInfo));

		return result;
	}

	@AfterThrowing(pointcut = "webLog()", throwing = "e")
	public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		assert attributes != null;
		HttpServletRequest request = attributes.getRequest();
		RequestErrorInfo requestErrorInfo = new RequestErrorInfo();
		requestErrorInfo.setIp(request.getRemoteAddr());
		requestErrorInfo.setUrl(request.getRequestURL().toString());
		requestErrorInfo.setHttpMethod(request.getMethod());
		requestErrorInfo.setClassMethod(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(),
				joinPoint.getSignature().getName()));
		requestErrorInfo.setRequestParams(getRequestParamsByJoinPoint(joinPoint));
		requestErrorInfo.setException(e.getLocalizedMessage());
		log.info(JSONUtil.toJsonPrettyStr(requestErrorInfo));
	}


	/**
	 * 获取入参
	 *
	 * @param proceedingJoinPoint
	 * @return
	 */
	private Map<String, Object> getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
		//参数名
		String[] paramNames = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterNames();
		//参数值
		Object[] paramValues = proceedingJoinPoint.getArgs();

		return buildRequestParam(paramNames, paramValues);
	}

	private Map<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
		//参数名
		String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
		//参数值
		Object[] paramValues = joinPoint.getArgs();

		return buildRequestParam(paramNames, paramValues);
	}


	private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
		Map<String, Object> requestParams = new HashMap<>();
		for (int i = 0; i < paramNames.length; i++) {
			Object value = paramValues[i];

			//如果是文件对象
			if (value instanceof MultipartFile) {
				MultipartFile file = (MultipartFile) value;
				//获取文件名
				value = file.getOriginalFilename();
			}

			boolean isResponse = (value instanceof HttpServletResponse);
			boolean isRequest = (value instanceof HttpServletRequest);
			if (!isRequest && !isResponse) {
				requestParams.put(paramNames[i], value);
			}
		}
		return requestParams;
	}

	@Data
	public class RequestErrorInfo {
		private String ip;
		private String url;
		private String httpMethod;
		private String classMethod;
		private Object requestParams;
		private String exception;
	}

	@Data
	public class RequestInfo {
		private String ip;
		private String url;
		private String httpMethod;
		private String classMethod;
		private Object requestParams;
		private Object result;
		private Long timeCost;
	}
}

